//
//						Layout.h
//

#ifndef __NEWA_LAYOUT_H
#define __NEWA_LAYOUT_H

#include <vector>

namespace newa
{
	class RectListener
	{
	public:
		virtual void rectChanged() = 0;
	};

	class BigRect
	{
	public:
		void addListener( RectListener& lst )
		{
			for( int i = 0; i < m_listeners.size(); i++ ) {
				if( m_listeners[i] == &lst )
					return; // already exists
			}

			m_listeners.push_back( &lst );
		}

		void removeListener( RectListener& lst )
		{
			std::vector< RectListener * >::iterator It;
			for( It = m_listeners.begin(); It != m_listeners.end(); It++ ) {
				if( *It == &lst ) {
					m_listeners.erase( It );
					return;
				}
			}
		}

		virtual Rect bigRect() const = 0;

	protected:
		void notifyAll( const Rect& r )
		{
			for( int i = 0; i < m_listeners.size(); i++ )
				m_listeners[i]->rectChanged();
		}
	private:
		std::vector< RectListener * > m_listeners;

	};

	class SmallRect
	{
	public:
		virtual Rect smallRect() const = 0;
		virtual void smallRect( const Rect& ) = 0;
		virtual ~SmallRect() {}
	};

	class LayoutManager : public RectListener
	{
		class Place; // defined later
	public:
		
		LayoutManager()
			: m_bigrect( 0 )
		{}

		void listen( BigRect& br)
		{
			m_bigrect = &br;
			br.addListener( *this );
			layout();
		}

		void manage( SmallRect& sr, int Place = 0 )
		{
			if( Place >= m_srects.size() ) {
				resize( Place + 1 );
			}
			
			m_srects[ Place ] = &sr;
			m_places[ Place ] = 0;

			layout();
		}

		void unmanage( SmallRect& sr )
		{
			for( int i = 0; i < size(); i++ ) {
				if( m_srects[i] == &sr )
					m_srects[i] = 0;
				if( m_places[i] == &sr )
					m_places[i] = 0;
			}
		}

		BigRect * at( int Index = 0 )
		{
			return m_places[ Index ];
		}

		void resize( int new_size )
		{
			if( new_size == size() )
				return;

			if( new_size < size() ) {
				for( int i = new_size; i < size(); i++ ) {
					if( m_places[ i ] != 0 )
						delete m_places[ i ];
				}
				m_srects.resize( new_size, 0 );
				m_places.resize( new_size, 0 );
			} else if ( new_size > size() ) {
				int old_size = m_srects.size();

				m_srects.resize( new_size, 0 );
				m_places.resize( new_size, 0 );
				for( int i = old_size; i < new_size - 1; i++ ) {
					Place * place = new Place;
					m_srects[ i ] = place;
					m_places[ i ] = place;
				}
			}

			layout();
		}

		int size() const
		{
			return m_srects.size();
		}

		virtual ~LayoutManager()
		{
			resize( 0 );
		}

		void layout()
		{
			if( m_bigrect != 0 )
				doLayout( m_bigrect->bigRect() );
		}

	protected:
		SmallRect * managable( int Index = 0 )
		{
			if( Index < 0 || Index >= m_srects.size() )
				return 0;

			return m_srects[ Index ];
		}

		virtual void rectChanged()
		{
			layout();
		}

		virtual void doLayout( const Rect& ) = 0;
		

	private:
		class Place : public BigRect, public SmallRect
		{
		public:
			virtual Rect bigRect() const
			{
				return m_rect;
			}

			virtual Rect smallRect() const
			{
				return m_rect;
			}

			virtual void smallRect( const Rect& r )
			{
				m_rect = r;
				notifyAll( r );
			}

		private:
			Rect m_rect;
		};

		BigRect * m_bigrect;

		std::vector< SmallRect * > m_srects;
		std::vector< Place * > m_places;
		
	};

	namespace Layouts
	{
		enum Side_E 
		{
			CENTER = 0, 
			LEFT = 1,
			RIGHT = 2, 
			TOP = 4,
			BOTTOM = 8
		};

		class Align : public LayoutManager
		{
		public:
			Align( unsigned Sides = 0)
			{
				resize(1);
				alignTo( Sides );
			}

			void alignTo( unsigned Sides )
			{
				m_sides = Sides;
			}

		protected:
			virtual void doLayout( const Rect& r )
			{
				if( managable() == 0 )
					return;

				Rect sr = managable()->smallRect();

				if( m_sides & LEFT ) {
					sr.x = r.x;
				} else if( m_sides & RIGHT ) {
					sr.x = r.x + r.width - sr.width;
				} else { // CENTER
					sr.x = r.x + ( r.width - sr.width ) / 2;
				}
				
				if( m_sides & TOP ) {
					sr.y = r.y;
				} else if( m_sides & BOTTOM ) {
					sr.y = r.y + r.height - sr.height;
				} else { // CENTER
					sr.y = r.y + ( r.height - sr.height ) / 2;
				}

				managable()->smallRect( sr );
			}

		private:
			unsigned m_sides;
		};

		class Row : public LayoutManager
		{
		public:
			Row( int Size = 1 )
			{
				resize( Size );
			}

		protected:
			virtual void doLayout( const Rect& r )
			{
				int n = size();
				for( int i = 0; i < size(); i++ ) {
					SmallRect * m = managable( i );
					if( m == 0 )
						continue;
					m->smallRect
						(
							Rect
								(
									r.x + r.width / n, 
									r.y,
									r.width / n,
									r.height
								)
						);
				}
			}
		};
	}
}

#endif
